const WebSocket = require('ws');
let fetchFn = null;
async function getFetch() {
    if (typeof fetch !== 'undefined') {
        return fetch.bind(globalThis);
    }
    if (!fetchFn) {
        const mod = await import('node-fetch');
        fetchFn = mod.default;
    }
    return fetchFn;
}

function parseArgs() {
    const args = process.argv.slice(2);
    const argMap = {};
    for (const a of args) {
        const m = /^--([^=]+)=(.*)$/.exec(a);
        if (m) argMap[m[1]] = m[2];
    }
    return {
        host: argMap.host || process.env.HOST || 'localhost',
        port: parseInt(argMap.port || process.env.PORT || '3000', 10),
        mobile: argMap.mobile || process.env.MOBILE || '17091271111',
        password: argMap.pwd || process.env.PWD || '123456',
        passwordHash: argMap.pwdHash || process.env.PWD_HASH || 'e10adc3949ba59abbe56e057f20f883e',
        roleId: argMap.roleId ? parseInt(argMap.roleId, 10) : (process.env.ROLE_ID ? parseInt(process.env.ROLE_ID, 10) : undefined),
    };
}

function wrapHostForUrl(host) {
    if (!host) return 'localhost';
    if (host.startsWith('[') && host.endsWith(']')) return host;
    if (host.includes(':')) return `[${host}]`;
    return host;
}

class VipClaimTester {
    constructor({ host, port, mobile, password, passwordHash, roleId }) {
        this.host = host;
        this.port = port;
        this.mobile = mobile;
        this.password = password;
        this.passwordHash = passwordHash;
        this.roleId = roleId;
        this.token = null;
        this.ws = null;
        this.msgQueue = [];
    }

    getHttpUrl(path) {
        const h = wrapHostForUrl(this.host);
        return `http://${h}:${this.port}${path}`;
    }

    async registerIfNeeded() {
        try {
            const fetch = await getFetch();
            const url = this.getHttpUrl('/user-service/user/register');
            const res = await fetch(url, {
                method: 'POST',
                headers: { 'content-type': 'application/json' },
                body: JSON.stringify({ mobile: this.mobile, pwd: this.passwordHash, captcha: '123456' }),
            });
            const data = await res.json().catch(() => ({}));
            if (res.ok) {
                console.log('📝 注册结果:', JSON.stringify(data));
            } else if (data && (data.code === 40001 || /存在|exist/i.test(String(data.msg || '')))) {
                console.log('👤 用户已存在，跳过注册');
            } else {
                console.log('⚠️ 注册可能失败(忽略):', JSON.stringify(data));
            }
        } catch (e) {
            console.log('⚠️ 注册请求异常(忽略):', e.message);
        }
    }

    async login() {
        await this.registerIfNeeded();
        const fetch = await getFetch();
        const url = this.getHttpUrl('/user-service/user/login');
        const res = await fetch(url, {
            method: 'POST',
            headers: { 'content-type': 'application/json' },
            body: JSON.stringify({ mobile: this.mobile, pwd: this.passwordHash }),
        });
        const data = await res.json();
        if (!(data && data.code === 0 && data.data?.accessToken)) throw new Error('login failed');
        this.token = data.data.accessToken;
        console.log('✅ 登录成功');
    }

    async connect() {
        await new Promise((resolve, reject) => {
            const h = wrapHostForUrl(this.host);
            const wsUrl = `ws://${h}:${this.port}/user-service/websocket?t=${this.token}`;
            this.ws = new WebSocket(wsUrl);
            const to = setTimeout(() => {
                try { this.ws && this.ws.close(); } catch { }
                reject(new Error('WebSocket connect timeout'));
            }, 10000);
            this.ws.on('open', () => { clearTimeout(to); resolve(); });
            this.ws.on('error', (e) => { clearTimeout(to); reject(e); });
            this.ws.on('message', (buf) => {
                try {
                    const msg = JSON.parse(buf.toString());
                    this.msgQueue.push(msg);
                } catch { }
            });
            this.ws.on('close', () => { });
        });
        console.log('🔌 WebSocket已连接');
    }

    async delay(ms) { return new Promise(r => setTimeout(r, ms)); }

    async sendWait(predicate, payload, timeoutMs = 15000) {
        if (payload) this.ws.send(JSON.stringify(payload));
        const start = Date.now();
        while (Date.now() - start < timeoutMs) {
            for (let i = 0; i < this.msgQueue.length; i++) {
                const m = this.msgQueue[i];
                if (predicate(m)) {
                    this.msgQueue.splice(i, 1);
                    return m;
                }
            }
            await this.delay(50);
        }
        throw new Error('timeout waiting message');
    }

    async queryRoles() {
        await this.delay(1000);
        const r = await this.sendWait(m => m.act_id === 3, { c: 3, d: null });
        const list = r.roleList || [];
        return list;
    }

    async selectRoleById(id) {
        await this.sendWait(m => m.act_id === 4, { c: 4, d: { characterId: id } });
    }

    async createCharacterIfNeeded() {
        let roles = await this.queryRoles();
        if (roles && roles.length > 0) return roles;
        const baseName = '测试角色';
        let name = baseName;
        for (let attempt = 0; attempt < 3; attempt++) {
            const res = await this.sendWait(
                m => m.act_id === 5 || m.act_id === 390,
                { c: 5, d: { name, sex: 1, roleType: 1, headIndex: 2 } },
                15000
            );
            if (res.code === 0) {
                console.log('🆕 已创建角色:', name);
                await this.delay(1200);
                break;
            }
            name = `${baseName}_${Date.now()}`;
            await this.delay(800);
        }
        roles = await this.queryRoles();
        return roles;
    }

    async queryMisc(tag) {
        const r = await this.sendWait(m => m.act_id === 1425, { c: 1425, d: {} });
        const arr = Array.isArray(r.data) ? r.data : [];
        console.log(`[1425 ${tag}] items=${arr.length}`);
        return arr;
    }

    async queryGems(tag) {
        const r = await this.sendWait(m => m.act_id === 6034, { c: 6034, d: { pageNum: 0 } });
        const total = typeof r.sum === 'number' ? r.sum : (Array.isArray(r.list) ? r.list.length : 0);
        console.log(`[6034 ${tag}] total=${total}`);
        return { total };
    }

    async queryDeputies(tag) {
        const r = await this.sendWait(m => m.act_id === 1119, { c: 1119, d: null });
        const total = Array.isArray(r.list) ? r.list.length : 0;
        console.log(`[1119 ${tag}] total=${total}`);
        return { total };
    }

    async queryVip(lvl) {
        const r = await this.sendWait(m => m.act_id === 6102, { c: 6102, d: { lvl } });
        return r.data;
    }

    async claimItem(pid, lvl) {
        const r = await this.sendWait(m => m.act_id === 6153, { c: 6153, d: { pid, t: 1, lvl } });
        console.log(`[6153 item] lvl=${lvl} pid=${pid} -> code=${r.code} msg=${r.msg || ''} data=${JSON.stringify(r.data || null)}`);
        return r;
    }

    async claimGem(lvl) {
        const r = await this.sendWait(m => m.act_id === 6153, { c: 6153, d: { pid: 0, t: 3, lvl } });
        console.log(`[6153 gem] lvl=${lvl} -> code=${r.code} msg=${r.msg || ''}`);
        return r;
    }

    async claimDeputy(lvl, dl) {
        const r = await this.sendWait(m => m.act_id === 6153, { c: 6153, d: { pid: 0, t: 4, lvl, dl } });
        console.log(`[6153 deputy] lvl=${lvl} dl=${dl} -> code=${r.code} msg=${r.msg || ''}`);
        return r;
    }

    async run() {
        await this.login();
        await this.connect();
        const roles = await this.createCharacterIfNeeded();
        const role = this.roleId ? roles.find(r => r.id === this.roleId) : roles[0];
        if (!role) throw new Error('no role');
        await this.selectRoleById(role.id);

        // 动态扫描各等级，寻找可领取(state=1)的条目
        let itemTarget = null;
        let gemTargetLvl = null;
        let deputyTargetLvl = null;

        for (let lvl = 0; lvl <= 9; lvl++) {
            const data = await this.queryVip(lvl);
            const list = data?.list || [];
            if (!itemTarget) {
                const it = list.find(x => x.type === 1 && x.state === 1 && x.id > 0);
                if (it) itemTarget = { pid: it.id, lvl, qty: (() => { const m = /\((\d+)\)/.exec(it.title || ''); return m ? parseInt(m[1], 10) : 1; })() };
            }
            if (gemTargetLvl === null) {
                const gt = list.find(x => x.type === 3 && x.state === 1);
                if (gt) gemTargetLvl = lvl;
            }
            if (deputyTargetLvl === null) {
                const dt = list.find(x => x.type === 4 && x.state === 1 && !x.choose);
                if (dt) deputyTargetLvl = lvl;
            }
            if (itemTarget && gemTargetLvl !== null && deputyTargetLvl !== null) break;
        }

        // 1) 领取物品并校验数量+领取状态
        if (!itemTarget) throw new Error('未找到可领取的物品条目');
        const beforeMisc = await this.queryMisc('before');
        const beforeCount = (beforeMisc || []).find(x => x.id === itemTarget.pid)?.num || 0;
        const claimRes = await this.claimItem(itemTarget.pid, itemTarget.lvl);
        let afterCount = beforeCount;
        let afterNumEcho = claimRes?.data?.after;
        const claimedQty = claimRes?.data?.qty || itemTarget.qty;
        // 轮询背包，等待服务端异步落库
        for (let i = 0; i < 5; i++) {
            await this.delay(500 + i * 200);
            const afterMisc = await this.queryMisc(`after#${i + 1}`);
            afterCount = (afterMisc || []).find(x => x.id === itemTarget.pid)?.num || afterCount;
            if (afterCount - beforeCount >= claimedQty) break;
        }
        if (afterCount - beforeCount < claimedQty) {
            // 再次检查 6102 该等级领取状态是否已变为2(已领取)
            const data = await this.queryVip(itemTarget.lvl);
            const list = data?.list || [];
            const it2 = list.find(x => x.type === 1 && x.id === itemTarget.pid);
            const stateNow = it2 ? it2.state : -1;
            if (stateNow === 2 && claimRes?.code === 0) {
                console.log(`⚠️ 物品领取数量未即时反映，但状态已领取(state=2)，视为通过 (before=${beforeCount}, after=${afterCount}, need>=${claimedQty})`);
            } else {
                throw new Error(`物品领取数量不足(state=${stateNow}, echoAfter=${afterNumEcho}, before=${beforeCount}, after=${afterCount}, need>=${claimedQty})`);
            }
        }

        // 2) 领取宝石并校验背包计数+1（6034 的 sum）
        if (gemTargetLvl === null) throw new Error('未找到可领取的宝石条目');
        const beforeGems = await this.queryGems('before');
        const beforeTotal = beforeGems.total || 0;
        await this.claimGem(gemTargetLvl);
        const afterGems = await this.queryGems('after');
        const afterTotal = afterGems.total || 0;
        if (afterTotal - beforeTotal < 1) throw new Error('宝石领取未增加');

        // 3) 领取副将（验证列表数量+1）
        if (deputyTargetLvl === null) throw new Error('未找到可领取的副将条目');
        const beforeDep = await this.queryDeputies('before');
        const depRes = await this.claimDeputy(deputyTargetLvl, 0);
        if (!(depRes && depRes.code === 0)) throw new Error('副将领取失败');
        let afterDep = beforeDep;
        for (let i = 0; i < 6; i++) {
            await this.delay(500 + i * 250);
            afterDep = await this.queryDeputies(`after#${i + 1}`);
            if ((afterDep.total - beforeDep.total) >= 1) break;
        }
        if ((afterDep.total - beforeDep.total) < 1) {
            const data = await this.queryVip(deputyTargetLvl);
            const list = data?.list || [];
            const it = list.find(x => x.type === 4);
            const stateNow = it ? it.state : -1;
            console.log(`⚠️ 副将数量未即时增加（before=${beforeDep.total}, after=${afterDep.total}, state=${stateNow}）。可能进入仓库或状态未刷新，继续后续校验。`);
        }

        console.log('All VIP claim tests passed.');
    }
}

(async () => {
    const cfg = parseArgs();
    const tester = new VipClaimTester(cfg);
    try {
        await tester.run();
        process.exit(0);
    } catch (e) {
        console.error('❌ VIP测试失败:', e.message);
        process.exit(1);
    }
})();
